package com.cctc.security.domain;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.ColumnResult;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.Lob;
import javax.persistence.ManyToMany;
import javax.persistence.SqlResultSetMapping;
import javax.persistence.SqlResultSetMappings;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.NamedNativeQueries;
import org.hibernate.annotations.NamedNativeQuery;
import org.hibernate.annotations.NamedQuery;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.Assert;

import com.cctc.framework.bean.BaseObj;
import com.cctc.security.util.SqlUtil;

@Entity
@Table(name = "CCTCT_USERS")
@NamedQuery(name = "listUserByAccount", query = "FROM User u WHERE u.account=?")
@NamedNativeQueries(value={
        @NamedNativeQuery(name="getNameByAccount",query=SqlUtil.SQL_USER_GET_NAME_BY_ACCOUNT,resultSetMapping="resNameByAccount")}
        )
@SqlResultSetMappings(value={
        @SqlResultSetMapping(name="resNameByAccount",columns={@ColumnResult(name="NAME")})}
        )
public class User extends BaseObj implements UserDetails{

	private static final long serialVersionUID = 5587823845788109578L;

	@Transient
	private Log log = LogFactory.getLog(User.class);

	public User() {
	    this.accountNonExpired=1;
	    this.accountNonLocked=1;
	    this.credentialsNonExpired=1;
	    this.enabled=1;
	    this.isSys=0;
	}

	public User(String account, String password, int enabled, int accountNonExpired, int credentialsNonExpired,
			int accountNonLocked, Collection<GrantedAuthority> auths) {
		if (((account == null) || "".equals(account)) || (password == null)) {
			log.error("Cannot pass null or empty values to constructor");
			throw new IllegalArgumentException("Cannot pass null or empty values to constructor");
		}
		this.account = account;
		this.password = password;
		this.enabled = enabled;
		this.accountNonExpired = accountNonExpired;
		this.accountNonLocked = accountNonLocked;
		this.credentialsNonExpired = credentialsNonExpired;
		this.auths = Collections.unmodifiableSet(sortAuthorities(auths));
	}

	@Id
    @Column(length=36,nullable=false)
    @GeneratedValue(generator = "generator")
    @GenericGenerator(name = "generator", strategy = SqlUtil.HIBERNATE_UUID)
	private String id;

	@Column(name = "ACCOUNT", length = 30, unique = true, nullable = false)
	private String account;

	@Column(name = "NAME", length = 20, nullable = false)
	private String name;

	@Column(name = "PASSWORD", length = 32, nullable = false)
	private String password;

	@Column(name = "EMAIL", length = 50)
	private String email;

	@Column(name = "ENABLED", nullable = false,columnDefinition="INT DEFAULT 1")
	private int enabled;

	@Column(name = "IS_SYS", nullable = false,columnDefinition="INT DEFAULT 0")
	private int isSys;

	@Column(name = "ACCOUNT_NON_EXPIRED", nullable = false,columnDefinition="INT DEFAULT 1")
	private int accountNonExpired;

	@Column(name = "ACCOUNT_NON_LOCKED", nullable = false,columnDefinition="INT DEFAULT 1")
	private int accountNonLocked;
	
	@Lob
	@Basic(fetch=FetchType.LAZY)
	@Column(name="IMAGE")
	private byte[] image;

	@Column(name = "CREDENTIALS_NON_EXPIRED", nullable = false,columnDefinition="INT DEFAULT 1")
	private int credentialsNonExpired;

	//表示在数据库中将该字段忽略
	@Transient
	private Set<GrantedAuthority> auths;

	//mappedBy="listUsers",
	@ManyToMany(cascade = CascadeType.ALL)
	@JoinTable(name = "CCTCT_USERS_GROUPS", joinColumns = { @JoinColumn(name = "USER_ID") }, inverseJoinColumns = { @JoinColumn(name = "GROUP_ID") })
	private List<Group> listGroups = new ArrayList<Group>();

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getAccount() {
		return account;
	}

	public void setAccount(String account) {
		this.account = account;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public int getEnabled() {
		return enabled;
	}

	public void setEnabled(int enabled) {
		this.enabled = enabled;
	}

	public int getIsSys() {
		return isSys;
	}

	public void setIsSys(int isSys) {
		this.isSys = isSys;
	}

	public List<Group> getListGroups() {
		return listGroups;
	}

	public void setListGroups(List<Group> listGroups) {
		this.listGroups = listGroups;
	}

	@Override
	public Collection<GrantedAuthority> getAuthorities() {
		return getAuths();
	}

	public Set<GrantedAuthority> getAuths() {
		return auths;
	}

	public void setAuths(Set<GrantedAuthority> auths) {
		this.auths = auths;
	}

	@Override
	public String getUsername() {
		return getAccount();
	}

	@Override
	public boolean isAccountNonExpired() {
	   return  (1 == getAccountNonExpired())?true:false;
	}

	@Override
	public boolean isAccountNonLocked() {
	    return  (1 == getAccountNonLocked())?true:false;
	}

	@Override
	public boolean isCredentialsNonExpired() {
	    return  (1 == getCredentialsNonExpired())?true:false;
	}

	@Override
	public boolean isEnabled() {
	    return  (1 == getEnabled())?true:false;
	}

	public int getAccountNonExpired() {
		return accountNonExpired;
	}

	public void setAccountNonExpired(int accountNonExpired) {
		this.accountNonExpired = accountNonExpired;
	}

	public int getAccountNonLocked() {
		return accountNonLocked;
	}

	public void setAccountNonLocked(int accountNonLocked) {
		this.accountNonLocked = accountNonLocked;
	}

	public int getCredentialsNonExpired() {
		return credentialsNonExpired;
	}

	public void setCredentialsNonExpired(int credentialsNonExpired) {
		this.credentialsNonExpired = credentialsNonExpired;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}
	
	public byte[] getImage()
    {
        return image;
    }

    public void setImage(byte[] image)
    {
        this.image = image;
    }

    private static SortedSet<GrantedAuthority> sortAuthorities(Collection<GrantedAuthority> authorities) {
		Assert.notNull(authorities, "Cannot pass a null GrantedAuthority collection");
		// Ensure array iteration order is predictable (as per UserDetails.getAuthorities() contract and SEC-717)
		SortedSet<GrantedAuthority> sortedAuthorities = new TreeSet<GrantedAuthority>(new AuthorityComparator());

		for (GrantedAuthority grantedAuthority : authorities) {
			Assert.notNull(grantedAuthority, "GrantedAuthority list cannot contain any null elements");
			sortedAuthorities.add(grantedAuthority);
		}

		return sortedAuthorities;
	}

	private static class AuthorityComparator implements Comparator<GrantedAuthority>, Serializable {
		private static final long serialVersionUID = 2198223032642500232L;

		@Override
		public int compare(GrantedAuthority g1, GrantedAuthority g2) {
			// Neither should ever be null as each entry is checked before adding it to the set.
			// If the authority is null, it is a custom authority and should precede others.
			if (g2.getAuthority() == null) {
				return -1;
			}

			if (g1.getAuthority() == null) {
				return 1;
			}

			return g1.getAuthority().compareTo(g2.getAuthority());
		}
	}


}
